#ifndef TREE_NODE_EXTERN_H
#define TREE_NODE_EXTERN_H
#include <iostream>
#include "TreeNode.h"
using namespace std;

extern FILE *fout;

class StmtNode : public TreeNode {

public:
    StmtNode(int line):TreeNode(line){

    };

    void generate_code() override{
    
        for(auto &child:this->children){
            child->generate_code();
        }
    
    }
};



class IfNode : public TreeNode {
public:
    static int if_count ;
    IfNode(int line, TreeNode* condition, TreeNode* true_stmt, TreeNode* false_stmt):TreeNode(line){};

    IfNode(int line, TreeNode* condition, TreeNode* true_stmt):TreeNode(line,condition,true_stmt){};

    void generate_code() override{
        if_count++;
        this->children[0]->generate_code();
        if(this->children.size()==3){
            //if else
            fprintf(fout,"if_else_%d:\n",if_count);
            this->children[1]->generate_code();
            fprintf(fout,"jmp if_end_%d\n",if_count);

            fprintf(fout,"label if_else_%d\n",if_count);
            this->children[2]->generate_code();

            fprintf(fout,"label if_end_%d\n",if_count);
        }else{
            //if
            fprintf(fout,"if_end_%d:\n",if_count);
            this->children[1]->generate_code();
            fprintf(fout,"label if_end_%d\n",if_count);
        }
          fflush(fout);
    }
};
int IfNode::if_count = 0;

class RepeatNode : public TreeNode {
public:
    static int repeat_count ;
    RepeatNode(int line, TreeNode* condition, TreeNode* stmt):TreeNode(line,condition,stmt){};

    void generate_code() override{
        repeat_count++;
        fprintf(fout,"label repeat_%d\n",repeat_count);
        this->children[0]->generate_code();
        this->children[1]->generate_code();
        fprintf(fout,"repeat_%d\n",repeat_count);
        fflush(fout);
    }
};
int RepeatNode::repeat_count = 0;

class AssignNode : public TreeNode {
public:
    AssignNode(int line, TreeNode* left, TreeNode* right):TreeNode(line,left,right){};

    void generate_code() override{
        this->children[1]->generate_code();
        fprintf(fout,"pop %s\n",this->children[0]->attr.name);
          fflush(fout);
    }
};

class ReadNode : public TreeNode {
public:
    ReadNode(int line, TreeNode* left):TreeNode(line,left){};

    void generate_code() override{
        fprintf(fout,"read %s\n",children[0]->attr.name);
        fflush(fout);
    }
};

class WriteNode : public TreeNode {
public:
    WriteNode(int line, TreeNode* right):TreeNode(line,right){};

    void generate_code() override{
        fprintf(fout,"write %s\n",children[0]->attr.name);
    }
};

class ExpNode : public TreeNode {
public:
    ExpNode(int line, TreeNode* left, TreeNode* right):TreeNode(line,left,right){};
    ExpNode(int line, TreeNode* simple_exp):TreeNode(line,simple_exp){};
    ExpNode(int line):TreeNode(line){};
    void generate_code() override{
        if(this->children.size()==1){
            this->children[0]->generate_code();
        }
        else if(this->children.size()==2){
            //this->show_all_info();
            //cout<<endl;
            this->children[0]->generate_code();
            this->children[1]->generate_code();
            fprintf(fout,"sub\n");
            switch (this->attr.op)
            {
            case EQ:
                fprintf(fout,"jnz ");
                break;
            case LT:
                fprintf(fout,"jeg ");
                break;
            default:
                cerr<<"wrong operator in exp node"<<endl;
                break;
            }
        }
        fflush(fout);
    
    }
};

class SimpleExpNode : public TreeNode {
public:
    SimpleExpNode(int line, TreeNode* left, TreeNode* right,Token_Kind op):TreeNode(line,left,right){
        this->attr.op = op;
        this->kind = ExpK;
        this->type = Integer;
        this->production_kind.exp=OpK;
    };
    SimpleExpNode(int line, TreeNode* factor):TreeNode(line,factor){
        this->kind = ExpK;
        this->type = Integer;
        this->production_kind.exp=OpK;
    };

    void generate_code() override{
        if(this->children.size()==1){
            this->children[0]->generate_code();
        }else{
            switch (this->attr.op)
            {
            case PLUS:
                this->children[0]->generate_code();
                this->children[1]->generate_code();
                fprintf(fout,"add\n");
                break;
            case MINUS:
                this->children[0]->generate_code();
                this->children[1]->generate_code();
                fprintf(fout,"sub\n");
                break;
            default:
                cerr<<"wrong operator in simple exp node"<<endl;
                break;
            }
        }
        fflush(fout);
    }
};

class TermNode : public TreeNode {
public:
    TermNode(int line, TreeNode* left, TreeNode* right):TreeNode(line,left,right){};
    TermNode(int line, TreeNode* factor):TreeNode(line,factor){};
    void generate_code() override{
        if(this->children.size()==0){
            this->children[0]->generate_code();
        }else{
            this->children[0]->generate_code();
            this->children[1]->generate_code();
            switch (this->attr.op)
            {
            case TIMES:
                fprintf(fout,"mul\n");
                break;
            case OVER:
                fprintf(fout,"div\n");
                break;
            default:
                cerr<<"wrong operator in term node"<<endl;
                break;
            }
        }
        fflush(fout);
    }
};

class IdNode : public TreeNode {
public:
    IdNode(int line,const char *name):TreeNode(line){
        this->kind = ExpK;
        this->type = Integer;
        this->attr.name = new char[strlen(name)+1];
        strcpy(this->attr.name,name);
        this->production_kind.exp=IdK;
    }
    void generate_code() override{
        fprintf(fout,"push %s\n",this->attr.name);
        fflush(fout);
    }
};

class NumNode : public TreeNode {
    public:
    NumNode(int line,int value):TreeNode(line){
        this->kind = ExpK;
        this->type = Integer;
        this->attr.val = value;
        this->production_kind.exp=ConstK;
    }
    void generate_code() override{
        fprintf(fout,"push $%d\n",this->attr.val);
        fflush(fout);
    }
};
#endif